﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
using System.Workflow.ComponentModel.Compiler;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using System.Web.Services.Protocols;
using System.Diagnostics;

namespace CrmContrib.Workflow.Activities.Opportunities
{
    [CrmWorkflowActivity("Close opportunities", "CrmContrib \\ Opportunities")]
    public class CloseOpportunitiesActivity : Activity
    {
        private ICrmService _CrmService;

        [CrmInput("On or Before this date")]
        [ValidationOption(ValidationOption.Required)]
        public CrmDateTime Age
        {
            get { return (CrmDateTime)base.GetValue(AgeProperty); }
            set { base.SetValue(AgeProperty, value); }
        }

        public static readonly DependencyProperty AgeProperty = DependencyProperty.Register("Age", typeof(CrmDateTime), typeof(CloseOpportunitiesActivity));

        private IEnumerable<SalesOrderSummary> SelectFrom(IEnumerable<BusinessEntity> entities)
        {
            foreach (BusinessEntity entity in entities)
            {
                Guid salesOrderId = new Guid();
                CrmMoney amount = new CrmMoney(0m);
                if (((DynamicEntity)entity).Properties.Contains("salesorderid"))
                {
                    salesOrderId = ((Key)((DynamicEntity)entity)["salesorderid"]).Value;
                }
                if (((DynamicEntity)entity).Properties.Contains("totalamount"))
                {
                    amount = ((CrmMoney)((DynamicEntity)entity)["totalamount"]);
                }
                yield return new SalesOrderSummary { OrderId = salesOrderId, Amount = amount, InvoiceCount = 0 };
            }
        }

        private IEnumerable<SalesOrderSummary> LoadSalesOrder(Lookup opportunity)
        {
            var query = new QueryExpression { EntityName = EntityName.salesorder.ToString(), ColumnSet = new AllColumns() };
            query.Criteria.AddCondition("opportunityid", ConditionOperator.Equal, opportunity.Value);

            var response = (RetrieveMultipleResponse)_CrmService.Execute(new RetrieveMultipleRequest { ReturnDynamicEntities = true, Query = query });
            return SelectFrom(response.BusinessEntityCollection.BusinessEntities);
        }

        private void MarkAsLost(Lookup opportunity, DateTime closeDate)
        {
            var close = new opportunityclose { opportunityid = opportunity, actualend = new CrmDateTime(closeDate.ToString("u")) };
            var markAsLostRequest = new LoseOpportunityRequest { OpportunityClose = close, Status = -1 };

            try
            {
                _CrmService.Execute( markAsLostRequest );
            }
            catch ( SoapException ex )
            {
                Trace.TraceWarning( string.Format( "The opportunity could not be marked as lost ({0})", ex.Message ) );
            }
        }

        private void MarkAsWon(Lookup opportunity, Nullable<System.Decimal> amount, DateTime closeDate)
        {
            var close = new opportunityclose { opportunityid = opportunity, actualrevenue = amount.HasValue ? new CrmMoney(amount.Value) : new CrmMoney(0.00m), actualend = new CrmDateTime(closeDate.ToString("u")) };
            var markAsWonRequest = new WinOpportunityRequest { OpportunityClose = close, Status = -1 };

            try
            {
                _CrmService.Execute( markAsWonRequest );
            }
            catch ( SoapException ex )
            {
                Trace.TraceWarning( string.Format( "The opportunity could not be closed ({0})", ex.Message ) );   
            }
        }

        private void CloseOpportunity(Lookup opportunity, DateTime closeDate)
        {
            var orders = new List<SalesOrderSummary>(LoadSalesOrder(opportunity));
            if (orders.Count == 0)
                MarkAsLost(opportunity, closeDate);
            else
                MarkAsWon(opportunity, orders.Sum(x => x.Amount.Value), closeDate);
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            var contextService = executionContext.GetService<IContextService>();
            var workflowContext = contextService.Context;
            _CrmService = workflowContext.CreateCrmService();

            var query = new QueryExpression { EntityName = EntityName.opportunity.ToString(), ColumnSet = new ColumnSet() };
            var createdOn =  new ConditionExpression("createdon", ConditionOperator.OnOrBefore, Age);
            var statecode = new ConditionExpression("statecode", ConditionOperator.Equal, (int)OpportunityState.Open);
            var filter = new FilterExpression { FilterOperator = LogicalOperator.And };
            filter.AddCondition(createdOn);
            filter.AddCondition(statecode);

            query.Criteria.AddFilter(filter);

            var response = (RetrieveMultipleResponse)_CrmService.Execute(new RetrieveMultipleRequest { ReturnDynamicEntities = true, Query = query });
            var opportunities = response.BusinessEntityCollection.BusinessEntities.Select(x => new Lookup("opportunity", ((Key)((DynamicEntity)x)["opportunityid"]).Value));
            foreach (var item in opportunities)
            {
                CloseOpportunity(item, DateTime.Now);
            }

            return base.Execute(executionContext);
        }
    }
}
